<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>RTC推流管理 - LiveTalking数字人交互平台</title>
    <!-- 引入Bootstrap 5.3.0 -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
    <!-- 引入jQuery 3.6.0 -->
    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <!-- 引入Bootstrap JS -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
    <!-- 引入SRS SDK (WHIP推流器) -->
    <script src="https://ossrs.net/players/srsRtcPublisher-2.0.148.js"></script>
    <!-- 引入mpegts.js -->
    <script src="https://cdn.jsdelivr.net/npm/mpegts.js@1.7.3/dist/mpegts.js"></script>
    <style>
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
        }
        
        body {
            background: linear-gradient(135deg, #1a2a6c, #b21f1f, #1a2a6c);
            background-size: 400% 400%;
            animation: gradientBG 15s ease infinite;
            color: #fff;
            line-height: 1.6;
            padding: 20px;
            min-height: 100vh;
        }
        
        @keyframes gradientBG {
            0% { background-position: 0% 50%; }
            50% { background-position: 100% 50%; }
            100% { background-position: 0% 50%; }
        }
        
        .container {
            max-width: 1200px;
            margin: 0 auto;
            background: rgba(0, 5, 30, 0.85);
            border-radius: 15px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
            overflow: hidden;
            border: 1px solid rgba(100, 150, 255, 0.3);
        }
        
        header {
            background: linear-gradient(to right, #0f0c29, #302b63, #24243e);
            padding: 20px 30px;
            text-align: center;
            border-bottom: 1px solid #4a5fc2;
        }
        
        h1 {
            font-size: 2.2rem;
            margin-bottom: 10px;
            text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
            background: linear-gradient(to right, #4facfe, #00f2fe);
            -webkit-background-clip: text;
            background-clip: text;
            color: transparent;
        }
        
        .subtitle {
            color: #a0c8ff;
            font-size: 1.1rem;
            max-width: 800px;
            margin: 0 auto;
        }
        
        .content {
            padding: 25px;
        }
        
        .section {
            background: rgba(10, 20, 50, 0.6);
            border-radius: 12px;
            padding: 20px;
            margin-bottom: 25px;
            border: 1px solid rgba(100, 150, 255, 0.2);
        }
        
        .section-title {
            color: #4facfe;
            font-size: 1.4rem;
            margin-bottom: 15px;
            padding-bottom: 10px;
            border-bottom: 1px solid rgba(100, 150, 255, 0.3);
        }
        
        .form-group {
            margin-bottom: 20px;
        }
        
        .form-group label {
            display: block;
            margin-bottom: 8px;
            color: #a0c8ff;
            font-weight: 500;
        }
        
        .form-control {
            width: 100%;
            padding: 12px 15px;
            border-radius: 8px;
            border: 1px solid #4a5fc2;
            background: rgba(10, 20, 50, 0.7);
            color: #fff;
            font-size: 1rem;
            transition: border-color 0.3s;
        }
        
        .form-control:focus {
            outline: none;
            border-color: #4facfe;
            box-shadow: 0 0 0 2px rgba(79, 172, 254, 0.3);
        }
        
        .form-row {
            display: flex;
            gap: 20px;
            margin-bottom: 20px;
        }
        
        .form-col {
            flex: 1;
        }
        
        .button-group {
            display: flex;
            gap: 15px;
            flex-wrap: wrap;
            margin-top: 20px;
        }
        
        .control-btn {
            background: linear-gradient(to right, #4facfe, #00f2fe);
            color: #0f0c29;
            border: none;
            border-radius: 8px;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s ease;
            padding: 10px 20px;
            font-size: 1.1rem;
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
        }
        
        .control-btn:hover {
            transform: translateY(-2px);
            box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
            filter: brightness(1.05);
        }
        
        .control-btn:active {
            transform: translateY(0);
        }
        
        .control-btn[disabled] {
            background: #555;
            cursor: not-allowed;
            transform: none;
            box-shadow: none;
        }
        
        .btn-danger {
            background: linear-gradient(to right, #ff4757, #ff6b81);
            color: white;
        }
        
        .btn-success {
            background: linear-gradient(to right, #2ed573, #1dd1a1);
            color: #0f0c29;
        }
        
        .status-indicator {
            display: inline-flex;
            align-items: center;
            gap: 8px;
            padding: 8px 15px;
            border-radius: 20px;
            background: rgba(0, 0, 0, 0.3);
            font-size: 0.95rem;
            margin-bottom: 20px;
        }
        
        .status-dot {
            width: 12px;
            height: 12px;
            border-radius: 50%;
            background: #ff4757;
        }
        
        .status-dot.connected {
            background: #2ed573;
            box-shadow: 0 0 8px #2ed573;
        }
        
        .status-dot.connecting {
            background: #ffa502;
            animation: pulse 1.5s infinite;
        }
        
        @keyframes pulse {
            0% { box-shadow: 0 0 0 0 rgba(255, 165, 2, 0.7); }
            70% { box-shadow: 0 0 0 10px rgba(255, 165, 2, 0); }
            100% { box-shadow: 0 0 0 0 rgba(255, 165, 2, 0); }
        }
        
        .connection-info {
            background: rgba(0, 0, 0, 0.2);
            border-radius: 8px;
            padding: 12px;
            margin-top: 15px;
            font-size: 0.9rem;
        }
        
        .info-item {
            display: flex;
            justify-content: space-between;
            margin-bottom: 6px;
        }
        
        .info-label {
            color: #a0c8ff;
            font-weight: 500;
        }
        
        .video-container {
            position: relative;
            border-radius: 12px;
            overflow: hidden;
            background: #000;
            box-shadow: 0 8px 25px rgba(0, 0, 0, 0.4);
            aspect-ratio: 16/9;
            margin-top: 20px;
        }
        
        #video-preview {
            width: 100%;
            height: 100%;
            background: #000;
        }
        
        .stream-list {
            background: rgba(0, 5, 30, 0.7);
            border-radius: 10px;
            padding: 15px;
            margin-top: 15px;
            border: 1px solid rgba(100, 150, 255, 0.2);
            max-height: 300px;
            overflow-y: auto;
        }
        
        .stream-item {
            padding: 12px 15px;
            border-radius: 8px;
            background: rgba(100, 150, 255, 0.1);
            margin-bottom: 10px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        
        .stream-item.active {
            background: rgba(79, 172, 254, 0.2);
            border: 1px solid #4facfe;
        }
        
        .stream-info {
            flex: 1;
        }
        
        .stream-name {
            font-weight: 600;
            color: #4facfe;
            margin-bottom: 5px;
        }
        
        .stream-details {
            font-size: 0.9rem;
            color: #a0c8ff;
        }
        
        .stream-actions {
            display: flex;
            gap: 10px;
        }
        
        .action-btn {
            background: rgba(100, 150, 255, 0.2);
            color: #4facfe;
            border: none;
            border-radius: 5px;
            padding: 5px 10px;
            cursor: pointer;
            transition: all 0.3s ease;
        }
        
        .action-btn:hover {
            background: rgba(79, 172, 254, 0.3);
        }
        
        .error-message {
            background: #ff4757;
            color: white;
            padding: 15px;
            border-radius: 8px;
            margin-top: 15px;
            display: none;
            animation: fadeIn 0.3s;
        }
        
        @keyframes fadeIn {
            from { opacity: 0; transform: translateY(-10px); }
            to { opacity: 1; transform: translateY(0); }
        }
        
        footer {
            text-align: center;
            padding: 20px;
            color: #a0c8ff;
            font-size: 0.9rem;
            border-top: 1px solid rgba(100, 150, 255, 0.2);
            margin-top: 10px;
        }
        
        @media (max-width: 768px) {
            .form-row {
                flex-direction: column;
                gap: 0;
            }
            
            h1 {
                font-size: 1.8rem;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1>RTC推流管理</h1>
            <p class="subtitle">LiveTalking数字人交互平台 - 实时音视频推流管理控制台</p>
        </header>
        
        <div class="content">
            <div class="section">
                <h2 class="section-title">推流配置</h2>
                <div class="form-row">
                    <div class="form-col">
                        <div class="form-group">
                            <label for="server-url">服务器地址</label>
                            <input type="text" id="server-url" class="form-control" value="http://localhost:1985/rtc/v1/whip/">
                        </div>
                    </div>
                    <div class="form-col">
                        <div class="form-group">
                            <label for="stream-name">流名称</label>
                            <input type="text" id="stream-name" class="form-control" value="live/livestream">
                        </div>
                    </div>
                </div>
                
                <div class="form-row">
                    <div class="form-col">
                        <div class="form-group">
                            <label for="stun-server">STUN服务器</label>
                            <select id="stun-server" class="form-control">
                                <option value="">自动选择 (推荐)</option>
                                <option value="stun:stun.l.google.com:19302">Google STUN 服务器</option>
                                <option value="stun:global.stun.twilio.com:3478?transport=udp">Twilio STUN 服务器</option>
                                <option value="stun:stun.services.mozilla.com">Mozilla STUN 服务器</option>
                            </select>
                        </div>
                    </div>
                    <div class="form-col">
                        <div class="form-group">
                            <label for="video-source">视频源</label>
                            <select id="video-source" class="form-control">
                                <option value="camera">摄像头</option>
                                <option value="screen">屏幕共享</option>
                                <option value="file">文件</option>
                            </select>
                        </div>
                    </div>
                </div>
                
                <div class="form-group">
                    <label for="bitrate">视频码率 (kbps)</label>
                    <input type="range" id="bitrate" class="form-control" min="100" max="5000" value="1500">
                    <div style="display: flex; justify-content: space-between; margin-top: 5px;">
                        <span>100</span>
                        <span id="bitrate-value">1500 kbps</span>
                        <span>5000</span>
                    </div>
                </div>
                
                <div class="button-group">
                    <button id="start-btn" class="control-btn">开始推流</button>
                    <button id="stop-btn" class="control-btn btn-danger" disabled>停止推流</button>
                    <button id="save-config-btn" class="control-btn btn-success">保存配置</button>
                </div>
                
                <div class="status-indicator">
                    <span class="status-dot" id="connection-status"></span>
                    <span id="status-text">等待推流...</span>
                </div>
                
                <div class="connection-info" id="connection-info" style="display:none;">
                    <div class="info-item">
                        <span class="info-label">服务器地址:</span>
                        <span id="server-address">http://<span id="hostname">localhost</span>:1985/rtc/v1/whip/</span>
                    </div>
                    <div class="info-item">
                        <span class="info-label">流名称:</span>
                        <span id="current-stream">live/livestream</span>
                    </div>
                    <div class="info-item">
                        <span class="info-label">推流状态:</span>
                        <span id="publish-state">未推流</span>
                    </div>
                    <div class="info-item">
                        <span class="info-label">码率:</span>
                        <span id="current-bitrate">1500 kbps</span>
                    </div>
                </div>
                
                <div class="error-message" id="error-message">
                    推流失败: 无法建立WebRTC连接，请检查网络或配置设置
                </div>
            </div>
            
            <div class="section">
                <h2 class="section-title">视频预览</h2>
                <div class="video-container">
                    <video id="video-preview" autoplay playsinline muted></video>
                </div>
            </div>
            
            <div class="section">
                <h2 class="section-title">流管理</h2>
                <div class="stream-list" id="stream-list">
                    <div class="stream-item active">
                        <div class="stream-info">
                            <div class="stream-name">live/livestream</div>
                            <div class="stream-details">状态: 推流中 | 观看人数: 5</div>
                        </div>
                        <div class="stream-actions">
                            <button class="action-btn">查看</button>
                            <button class="action-btn">停止</button>
                        </div>
                    </div>
                    <div class="stream-item">
                        <div class="stream-info">
                            <div class="stream-name">live/teststream</div>
                            <div class="stream-details">状态: 已停止 | 观看人数: 0</div>
                        </div>
                        <div class="stream-actions">
                            <button class="action-btn">查看</button>
                            <button class="action-btn">删除</button>
                        </div>
                    </div>
                    <div class="stream-item">
                        <div class="stream-info">
                            <div class="stream-name">live/mobile</div>
                            <div class="stream-details">状态: 推流中 | 观看人数: 12</div>
                        </div>
                        <div class="stream-actions">
                            <button class="action-btn">查看</button>
                            <button class="action-btn">停止</button>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        
        <footer>
            LiveTalking 数字人交互平台 &copy; 2023 | 基于 WebRTC 和 SRS 实时流媒体技术
        </footer>
    </div>

    <script>
        $(document).ready(function() {
            // 全局变量
            let publisher = null;
            let publishState = 'disconnected'; // disconnected, connecting, connected, error
            let mediaStream = null;
            
            // 设置当前主机名
            const hostname = window.location.hostname || 'localhost';
            $('#hostname').text(hostname);
            
            // 初始化
            function init() {
                updatePublishState('disconnected');
                updateStatus('等待推流...', 'disconnected');
                $('#video-preview').hide();
                $('#connection-info').hide();
                $('#error-message').hide();
                $('#stop-btn').prop('disabled', true);
                
                // 初始化码率显示
                $('#bitrate-value').text($('#bitrate').val() + ' kbps');
                
                // 绑定码率滑块事件
                $('#bitrate').on('input', function() {
                    $('#bitrate-value').text($(this).val() + ' kbps');
                });
            }
            
            // 更新推流状态
            function updatePublishState(state) {
                publishState = state;
                $('#publish-state').text(
                    state === 'connected' ? '推流中' :
                    state === 'connecting' ? '连接中...' :
                    state === 'error' ? '推流错误' : '未推流'
                );
                
                // 更新状态指示器
                const statusDot = $('#connection-status');
                statusDot.removeClass('connected connecting');
                if (state === 'connected') {
                    statusDot.addClass('connected');
                } else if (state === 'connecting') {
                    statusDot.addClass('connecting');
                }
            }
            
            // 更新状态文本
            function updateStatus(text, type) {
                $('#status-text').text(text);
                
                if (type === 'error') {
                    $('#error-message').text(text).show();
                    setTimeout(() => {
                        $('#error-message').fadeOut(500);
                    }, 5000);
                } else {
                    $('#error-message').hide();
                }
            }
            
            // 开始推流
            async function startPublish() {
                if (publishState === 'connecting' || publishState === 'connected') {
                    return;
                }
                
                try {
                    // 获取媒体流
                    const videoSource = $('#video-source').val();
                    let constraints = { video: true, audio: true };
                    
                    if (videoSource === 'screen') {
                        mediaStream = await navigator.mediaDevices.getDisplayMedia({ 
                            video: true, 
                            audio: true 
                        });
                    } else {
                        mediaStream = await navigator.mediaDevices.getUserMedia(constraints);
                    }
                    
                    // 显示预览
                    $('#video-preview').show().attr('srcObject', mediaStream);
                    
                    updatePublishState('connecting');
                    updateStatus('正在建立WebRTC推流连接...', 'info');
                    $('#start-btn').prop('disabled', true).text('连接中...');
                    
                    // 获取配置
                    const serverUrl = $('#server-url').val();
                    const streamName = $('#stream-name').val();
                    const stunServer = $('#stun-server').val();
                    
                    // 获取STUN服务器配置
                    const iceServers = [];
                    if (stunServer) {
                        iceServers.push({ urls: stunServer });
                    } else {
                        // 默认STUN服务器
                        iceServers.push({ 
                            urls: [
                                'stun:stun.l.google.com:19302',
                                'stun:global.stun.twilio.com:3478?transport=udp'
                            ]
                        });
                    }
                    
                    // SRS WHIP 推流器配置
                    const config = {
                        stream: mediaStream,
                        url: serverUrl,
                        iceServers: iceServers,
                        api: serverUrl,
                        schema: 'http://',
                        port: 1985,
                        app: streamName.split('/')[0],
                        stream: streamName.split('/')[1]
                    };
                    
                    // 创建推流器实例
                    publisher = new SrsRtcPublisherAsync();
                    
                    // 设置事件监听
                    publisher.onconnect = function() {
                        console.log('Publisher connected');
                        updatePublishState('connected');
                        updateStatus('WebRTC推流已建立', 'success');
                        $('#connection-info').show();
                        $('#start-btn').text('推流中').prop('disabled', true);
                        $('#stop-btn').prop('disabled', false);
                        
                        // 更新当前流信息
                        $('#current-stream').text(streamName);
                        $('#current-bitrate').text($('#bitrate').val() + ' kbps');
                    };
                    
                    publisher.onpublish = function() {
                        console.log('Publisher started publishing');
                    };
                    
                    publisher.onerror = function(error) {
                        console.error('Publisher error:', error);
                        handlePublishError(error);
                    };
                    
                    // 开始推流
                    await publisher.publish(config);
                    console.log('Publisher started successfully');
                    
                } catch (error) {
                    console.error('Publisher initialization error:', error);
                    handlePublishError(error);
                }
            }
            
            // 停止推流
            function stopPublish() {
                if (publisher) {
                    publisher.close();
                    publisher = null;
                }
                
                if (mediaStream) {
                    mediaStream.getTracks().forEach(track => track.stop());
                    mediaStream = null;
                }
                
                $('#video-preview').hide().attr('srcObject', null);
                updatePublishState('disconnected');
                updateStatus('推流已停止', 'info');
                $('#start-btn').text('开始推流').prop('disabled', false);
                $('#stop-btn').prop('disabled', true);
                $('#connection-info').hide();
            }
            
            // 处理推流错误
            function handlePublishError(error) {
                updatePublishState('error');
                
                let errorMessage = 'WebRTC推流失败';
                if (error.message) {
                    errorMessage += ': ' + error.message;
                } else if (typeof error === 'string') {
                    errorMessage += ': ' + error;
                }
                
                updateStatus(errorMessage, 'error');
                $('#start-btn').prop('disabled', false).text('开始推流');
                $('#stop-btn').prop('disabled', true);
                
                // 显示错误消息
                $('#error-message').text(errorMessage).show();
            }
            
            // 保存配置
            function saveConfig() {
                const config = {
                    serverUrl: $('#server-url').val(),
                    streamName: $('#stream-name').val(),
                    stunServer: $('#stun-server').val(),
                    videoSource: $('#video-source').val(),
                    bitrate: $('#bitrate').val()
                };
                
                // 在实际应用中，这里会将配置保存到服务器或本地存储
                console.log('Saving configuration:', config);
                updateStatus('配置已保存', 'success');
                
                // 3秒后恢复原状态
                setTimeout(() => {
                    if (publishState !== 'connected') {
                        updateStatus('等待推流...', 'disconnected');
                    }
                }, 3000);
            }
            
            // 事件绑定
            $('#start-btn').click(startPublish);
            $('#stop-btn').click(stopPublish);
            $('#save-config-btn').click(saveConfig);
            
            // WebSocket连接功能
            let websocket = null;
            let websocketConnected = false;
            
            function connectWebSocket() {
                const wsUrl = `ws://${hostname}:8000/streammanager`;
                try {
                    websocket = new WebSocket(wsUrl);
                    
                    websocket.onopen = function() {
                        console.log('WebSocket connected');
                        websocketConnected = true;
                        updateStatus('WebSocket连接已建立', 'success');
                    };
                    
                    websocket.onmessage = function(event) {
                        console.log('WebSocket message received:', event.data);
                        // 处理接收到的消息
                        try {
                            const data = JSON.parse(event.data);
                            handleWebSocketMessage(data);
                        } catch (e) {
                            console.error('Failed to parse WebSocket message:', e);
                        }
                    };
                    
                    websocket.onclose = function() {
                        console.log('WebSocket disconnected');
                        websocketConnected = false;
                        updateStatus('WebSocket连接已断开', 'info');
                    };
                    
                    websocket.onerror = function(error) {
                        console.error('WebSocket error:', error);
                        updateStatus('WebSocket连接错误', 'error');
                    };
                } catch (error) {
                    console.error('Failed to connect WebSocket:', error);
                    updateStatus('WebSocket连接失败: ' + error.message, 'error');
                }
            }
            
            // 处理WebSocket消息
            function handleWebSocketMessage(data) {
                if (data.type === 'stream_update') {
                    // 更新流列表
                    updateStreamList(data.streams);
                } else if (data.type === 'notification') {
                    // 显示通知
                    updateStatus(data.message, 'info');
                }
            }
            
            // 更新流列表
            function updateStreamList(streams) {
                const streamList = $('#stream-list');
                streamList.empty();
                
                streams.forEach(stream => {
                    const streamItem = `
                        <div class="stream-item ${stream.active ? 'active' : ''}">
                            <div class="stream-info">
                                <div class="stream-name">${stream.name}</div>
                                <div class="stream-details">状态: ${stream.active ? '推流中' : '已停止'} | 观看人数: ${stream.viewers}</div>
                            </div>
                            <div class="stream-actions">
                                <button class="action-btn" onclick="viewStream('${stream.name}')">查看</button>
                                ${stream.active ? 
                                    `<button class="action-btn btn-danger" onclick="stopStream('${stream.name}')">停止</button>` :
                                    `<button class="action-btn" onclick="startStream('${stream.name}')">开始</button>`
                                }
                            </div>
                        </div>
                    `;
                    streamList.append(streamItem);
                });
            }
            
            // 查看流
            function viewStream(streamName) {
                updateStatus(`正在查看流: ${streamName}`, 'info');
                // 在实际应用中，这里会打开一个新的窗口或标签页来查看流
            }
            
            // 停止流
            function stopStream(streamName) {
                updateStatus(`正在停止流: ${streamName}`, 'info');
                // 在实际应用中，这里会发送请求到服务器停止流
            }
            
            // 开始流
            function startStream(streamName) {
                updateStatus(`正在开始流: ${streamName}`, 'info');
                // 在实际应用中，这里会发送请求到服务器开始流
            }
            
            // HTTP API调用示例
            async function callHttpApi(endpoint, method = 'GET', data = null) {
                try {
                    const url = `http://${hostname}:1985${endpoint}`;
                    const options = {
                        method: method,
                        headers: {
                            'Content-Type': 'application/json'
                        }
                    };
                    
                    if (data) {
                        options.body = JSON.stringify(data);
                    }
                    
                    const response = await fetch(url, options);
                    if (!response.ok) {
                        throw new Error(`HTTP error! status: ${response.status}`);
                    }
                    
                    const result = await response.json();
                    console.log('HTTP API call successful:', result);
                    return result;
                } catch (error) {
                    console.error('HTTP API call failed:', error);
                    updateStatus('API调用失败: ' + error.message, 'error');
                    throw error;
                }
            }
            
            // 获取流列表
            async function fetchStreamList() {
                try {
                    const result = await callHttpApi('/api/v1/streams', 'GET');
                    if (result.streams) {
                        updateStreamList(result.streams);
                    }
                } catch (error) {
                    console.error('Failed to fetch stream list:', error);
                }
            }
            
            // 初始化WebSocket连接
            connectWebSocket();
            
            // 定期获取流列表
            setInterval(fetchStreamList, 5000);
            
            // 初始化
            init();
        });
    </script>
</body>
</html>
